<?php

declare(strict_types=1);

namespace N98\Magento\Command\Database;

use InvalidArgumentException;
use N98\Magento\Command\TestCase;
use SplFileInfo;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;

/**
 * @see DumpCommand
 */
final class DumpCommandTest extends TestCase
{
    /**
     * @return Command
     */
    private function getCommand()
    {
        $dumpCommand = new DumpCommand();
        if (!$dumpCommand->isEnabled()) {
            self::markTestSkipped('DumpCommand is not enabled.');
        }

        $application = $this->getApplication();
        $application->add($dumpCommand);

        return $this->getApplication()->find('db:dump');
    }

    public function testExecute()
    {
        $command = $this->getCommand();

        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--compression'  => 'gz'],
        );

        $this->assertMatchesRegularExpression('/mysqldump/', $commandTester->getDisplay());
        $this->assertMatchesRegularExpression('/\.sql/', $commandTester->getDisplay());
        $this->assertStringContainsString('.sql.gz', $commandTester->getDisplay());
    }

    /**
     * @see filenamePatterns
     * @return \Iterator<array<int, (array<mixed> | string)>>
     */
    public function provideFilenamePatternsAndOptions(): \Iterator
    {
        yield 'testAddTimeAutogenerated'                => ['/^.*\d{4}-\d{2}-\d{2}_\d{6}\.sql$/', []];
        yield 'testAddTimePrefixAutogenerated'          => ['/^\d{4}-\d{2}-\d{2}_\d{6}.*\.sql$/', ['--add-time' => 'prefix']];
        yield 'testAddTimeCompressionFilenameSpecified' => ['/^.*\d{4}-\d{2}-\d{2}_\d{6}\.sql.gz$/', ['--compression' => 'gzip']];
        yield 'testAddTimeFilenameSpecified'            => ['/^foo_\d{4}-\d{2}-\d{2}_\d{6}\.sql$/', ['filename' => 'foo.sql']];
        yield 'testAddTimePrefixFilenameSpecified'      => ['/^\d{4}-\d{2}-\d{2}_\d{6}_foo\.sql$/', ['filename' => 'foo.sql', '--add-time' => 'prefix']];
        yield 'testAddTimeOffFilenameSpecified'         => ['/^foo.sql$/', ['filename' => 'foo.sql', '--add-time' => 'no']];
        yield 'testAddTimeFilenameSpecifiedRelative'    => ['/^..\/foo_\d{4}-\d{2}-\d{2}_\d{6}\.sql$/', ['filename' => '../foo.sql']];
    }

    /**
     * @dataProvider provideFilenamePatternsAndOptions
     *
     * @param string $regex
     * @return void
     * @param array<string, string> $options
     */
    public function testFilenamePatterns($regex, array $options)
    {
        $command = $this->getCommand();

        $mandatory = ['command'               => $command->getName(), '--force'               => true, '--print-only-filename' => true, '--dry-run'             => null];

        $defaults = ['--add-time' => true];

        $this->assertDisplayRegExp($mandatory + $options + $defaults, $regex);
    }

    public function testWithStripOption()
    {
        $command = $this->getCommand();

        $this->getApplication()->initMagento();

        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--strip'        => '@development not_existing_table_1', '--compression'  => 'gzip'],
        );

        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s.customer_entity/', $db), $commandTester->getDisplay());
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s.customer_address_entity/', $db), $commandTester->getDisplay());
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s.sales_flat_order/', $db), $commandTester->getDisplay());
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s.sales_flat_order_item/', $db), $commandTester->getDisplay());
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s.sales_flat_order_item/', $db), $commandTester->getDisplay());
        $this->assertStringNotContainsString('not_existing_table_1', $commandTester->getDisplay());
        $this->assertStringContainsString('.sql.gz', $commandTester->getDisplay());

        /**
         * Uncompressed
         */
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--strip'        => '@development'],
        );
        $this->assertStringNotContainsString('.sql.gz', $commandTester->getDisplay());
    }

    public function testWithIncludeExcludeOptions()
    {
        $command = $this->getCommand();
        $this->getApplication()->initMagento();
        $dbConfig = $this->getDatabaseConnection()->getConfig();
        $db = $dbConfig['dbname'];

        /**
         * Exclude
         */
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--exclude'      => 'core_config_data', '--compression'  => 'gzip'],
        );
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s\.core_config_data/', $db), $commandTester->getDisplay());

        /**
         * Include
         */
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--include'      => 'core_config_data', '--compression'  => 'gzip'],
        );
        $this->assertDoesNotMatchRegularExpression(sprintf('/--ignore-table=%s\.core_config_data/', $db), $commandTester->getDisplay());
        $this->assertMatchesRegularExpression(sprintf('/--ignore-table=%s\.catalog_product_entity/', $db), $commandTester->getDisplay());
    }

    public function testIncludeExcludeMutualExclusivity()
    {
        /**
         * Both include and exclude.
         */
        $command = $this->getCommand();
        $this->getApplication()->initMagento();
        $this->expectException(InvalidArgumentException::class);

        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'        => $command->getName(), '--add-time'     => true, '--only-command' => true, '--force'        => true, '--include'      => 'core_config_data', '--exclude'      => 'catalog_product_entity', '--compression'  => 'gzip'],
        );
    }

    /**
     * @link https://github.com/netz98/n98-magerun2/issues/200
     */
    public function testRealDump()
    {
        $dumpFile = new SplFileInfo($this->getTestMagentoRoot() . '/test-dump.sql');
        if ($dumpFile->isReadable()) {
            $this->assertTrue(unlink((string) $dumpFile), 'Precondition to unlink that the file does not exists');
        }

        $this->assertIsNotReadable((string) $dumpFile, 'Precondition that the file does not exists');

        $command = $this->getCommand();
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            ['command'  => $command->getName(), '--strip'  => '@stripped', 'filename' => $dumpFile],
        );

        $this->assertTrue($dumpFile->isReadable(), 'File was created');
        // dump should be larger than quarter a megabyte
        $this->assertGreaterThan(250000, $dumpFile->getSize());
    }
}
